package org.jvm.util;

import org.jvm.instruction.base.InstructionUtil;
import org.jvm.interpreter.Interpret;
import org.jvm.nativemethod.NativeMethodUtil;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.classmember.Method;
import org.jvm.rtda.thread.Thread;
import org.jvm.rtda.thread.*;

/**
 * 虚拟机发起javaCall
 *
 * @author 海燕
 * @date 2023/3/26
 */
public class JavaCallUtil {
    /**
     * 虚拟机调用java方法
     * TODO 暂时只支持引用参数和返回值 ，应当支持自动装箱和拆箱
     * 注意：在指令中调用本方法后，需要检查指令所占栈帧是否还在栈顶？如果不在栈顶说明javaCall期间出现了异常抛出，这时直接终止指令执行
     *
     * @param thread     调用线程
     * @param method     要调用的方法
     * @param thisObject 实例方法中this实例，静态方法填null
     * @param paramArray 参数列表
     * @return 返回值
     */
    public static java.lang.Object javaCall(Thread thread, Method method, Object thisObject, java.lang.Object... paramArray) {
        OperandStack operandStack = new OperandStack(paramArray.length * 2 + 1);
        //实例this入栈
        if (thisObject != null) {
            operandStack.pushRef(thisObject);
        }
        for (int i = 0; i < paramArray.length; i++) {
            operandStack.pushByType(paramArray[i]);
        }
        Frame shimFrame = NativeMethodUtil.newShimFrame(thread, operandStack);
        InstructionUtil.invokeMethod(shimFrame, method);
        //调用解释器在线程上执行java方法，间隙帧作为中止帧
        Interpret.interpret(thread);
        //正常调用完成后弹出间隙帧
        if (thread.getStack().top() == shimFrame) {
            thread.getStack().pop();
        }
        //间隙帧操作数栈顶就是方法的返回值
        if (shimFrame.getOperandStack().isEmpty()) {
            return null;
        }
        String returnTypeDescriptor = method.getReturnTypeDescriptor();
        return shimFrame.getOperandStack().popByType(returnTypeDescriptor);
    }


}
